# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: api\handlers\frontend\vulnerabilities.py
__author__ = "sanyi"
import base64, json, logging, uuid, os.path, pystache, requests
from sqlalchemy import and_ as sql_and
from sqlalchemy import desc as sql_desc
from sqlalchemy import func as sql_func
from sqlalchemy import or_ as sql_or
from sqlalchemy import select as sql_select
import helpers.issue_tracker.exceptions as issue_tracker_exceptions
from api.classes.frontend.auto import *
from api.filters import Filters
from api.filters.sqlalchemy_adapter import get_sqlachemy_filter
from api.handlers.frontend import FrontendResourceHandler
from db import Connection
from db.data.access import (
    generate_target_vulns_query,
    generate_target2group_query,
    apply_filters,
    get_distinct_targets_id_by_group,
)
from db.data.users import require_at_least_tester, get_owner_id
from db.tables.events import create_event, EventResources
from db.tables.target2group import Target2GroupRow
from db.tables.target_vulns import TargetVulnRow, TargetVulnsTable
from db.tables.targets import TargetRow, TargetsTable
from db.tables.targets_configurations import TargetConfigurationRow
from db.tables.issue_trackers import IssueTrackerRow
from db.tables.vuln_types import VulnTypeRow, VulnTypesTable
from helpers.issue_tracker.clients.client import get_client as issue_tracker_get_client
from server.http_responses.http_status_codes import *
from server.http_responses.http_errors import Http500, Http409, Http404, ApiErrors
from settings_provider import settings
from db.tables.vuln_types import sensor_details_template
from db.tables.scans import ScanRow, ScansTable
from db.tables.scan_sessions import ScanSessionRow, ScanSessionsTable
from helpers.custom_profiles import generate_profile_for_vuln_recheck
from helpers.licensing.features import Features
from helpers.setup_types import *
from helpers.address import get_local_addresses
from helpers.excluded_hours import (
    get_next_date_allowed_to_scan,
    is_allowed_to_scan_now,
    get_system_excluded_hours,
)
from db.views.targets_excluded_hours import (
    TargetsWithExcludedHoursViewRow,
    TargetsWithExcludedHoursViewTable,
)
from server.http_responses.file_download import FileDownload

logger = logging.getLogger("api.Vulnerabilities")
vulnerability_list_filters = {
    "keys": {
        "name": {
            "value_type": "string",
            "multiple_values": False,
            "operators": ("eq", "ilike_op"),
        },
        "severity": {
            "value_type": "int",
            "multiple_values": True,
            "operators": ("eq", "ne", "le", "ge", "lt", "gt"),
        },
        "criticality": {
            "value_type": "int",
            "multiple_values": True,
            "operators": ("eq", "ne", "le", "ge", "lt", "gt"),
        },
        "status": {
            "value_type": "string",
            "allowed_values": ("open", "fixed", "ignored", "false_positive"),
            "multiple_values": True,
            "operators": ("eq", "ne"),
        },
        "continuous": {"value_type": "boolean", "operators": ("eq", "ne")},
        "rediscovered": {"value_type": "boolean", "operators": ("eq", "ne")},
        "cvss_score": {
            "value_type": "float",
            "multiple_values": True,
            "operators": ("eq", "ne", "le", "ge", "lt", "gt"),
        },
        "target_id": {
            "value_type": "uuid4",
            "multiple_values": True,
            "operators": ("eq", "ne"),
        },
        "group_id": {"value_type": "uuid4", "multiple_values": True},
        "vt_id": {"value_type": "uuid4", "multiple_values": True},
        "date": {
            "value_type": "date",
            "multiple_values": True,
            "operators": ("eq", "ne", "le", "ge", "lt", "gt"),
        },
    }
}


class Vulnerabilities(FrontendResourceHandler):
    """
    This are the vulnerabilities grouped, stored and merged per target
    For the vulnerabilities per scan see Results/{result_id}
    """

    def get_vulnerabilities(self, l=None, c=None, q=None, **_):
        """
        Permissions: any_role & (access_all | access_group)
        List target vulns
        Default sorting order: Criticality, severity, vulnerability name, (vuln_id for consistent pagination)
        :param l:
        :param c:
        :param q:
        :param _:
        :return:
        """
        filters = Filters(vulnerability_list_filters, q)
        joined_tables = []
        select_from, tail_filters = generate_target_vulns_query(
            self.request_user, joined_tables
        )
        limit = min(l, 100) if l is not None else 100
        if c in ("0", 0, None):
            c = None
            cursor = None
        else:
            cursor = json.loads(base64.b64decode(c).decode())
        select_from = select_from.join(
            VulnTypesTable, VulnTypeRow.vt_id == TargetVulnRow.vt_id
        )
        joined_tables.append(VulnTypesTable)
        query = sql_select(
            (
                TargetVulnRow.last_seen,
                TargetVulnRow.first_seen,
                TargetVulnRow.status,
                TargetVulnRow.rediscovered,
                TargetVulnRow.tags,
                TargetVulnRow.vt_id,
                TargetVulnRow.target_id,
                TargetVulnRow.vuln_id,
                TargetVulnRow.loc_detail,
                TargetVulnRow.url,
                TargetVulnRow.criticality,
                TargetVulnRow.name,
                TargetVulnRow.continuous,
                VulnTypeRow.tags.label("vt_tags"),
                TargetVulnRow.severity,
                TargetVulnRow.issue_id,
                TargetRow.description.label("target_description"),
            )
        ).select_from(select_from)
        for filter_key, filter_expression in filters.data.items():
            flt = None
            if filter_key == "name":
                flt = get_sqlachemy_filter(
                    filter_expression,
                    TargetVulnRow.name,
                    filters.descriptor["keys"][filter_key],
                )
            else:
                if filter_key == "criticality":
                    flt = get_sqlachemy_filter(
                        filter_expression,
                        TargetVulnRow.criticality,
                        filters.descriptor["keys"][filter_key],
                    )
                else:
                    if filter_key == "severity":
                        flt = get_sqlachemy_filter(
                            filter_expression,
                            TargetVulnRow.severity,
                            filters.descriptor["keys"][filter_key],
                        )
                    else:
                        if filter_key == "status":
                            flt = get_sqlachemy_filter(
                                filter_expression,
                                TargetVulnRow.status,
                                filters.descriptor["keys"][filter_key],
                            )
                        else:
                            if filter_key == "rediscovered":
                                flt = get_sqlachemy_filter(
                                    filter_expression,
                                    TargetVulnRow.rediscovered,
                                    filters.descriptor["keys"][filter_key],
                                )
                            else:
                                if filter_key == "continuous":
                                    flt = get_sqlachemy_filter(
                                        filter_expression,
                                        TargetVulnRow.continuous,
                                        filters.descriptor["keys"][filter_key],
                                    )
                                else:
                                    if filter_key == "cvss_score":
                                        flt = get_sqlachemy_filter(
                                            filter_expression,
                                            VulnTypeRow.cvss_score,
                                            filters.descriptor["keys"][filter_key],
                                        )
                                    else:
                                        if filter_key == "vt_id":
                                            flt = get_sqlachemy_filter(
                                                filter_expression,
                                                VulnTypeRow.vt_id,
                                                filters.descriptor["keys"][filter_key],
                                            )
                                        else:
                                            if filter_key == "target_id":
                                                flt = get_sqlachemy_filter(
                                                    filter_expression,
                                                    TargetVulnRow.target_id,
                                                    filters.descriptor["keys"][
                                                        filter_key
                                                    ],
                                                )
                                            else:
                                                if filter_key == "date":
                                                    flt = get_sqlachemy_filter(
                                                        filter_expression,
                                                        TargetVulnRow.last_seen,
                                                        filters.descriptor["keys"][
                                                            filter_key
                                                        ],
                                                    )
                                                else:
                                                    if filter_key == "group_id":
                                                        flt = get_sqlachemy_filter(
                                                            filter_expression,
                                                            Target2GroupRow.group_id,
                                                            filters.descriptor["keys"][
                                                                filter_key
                                                            ],
                                                        )
                                                        sf, tf = generate_target2group_query(
                                                            self.request_user
                                                        )
                                                        sq = sql_select(
                                                            (Target2GroupRow.target_id,)
                                                        ).select_from(sf)
                                                        sq = apply_filters(sq, tf)
                                                        flt = TargetVulnRow.target_id.in_(
                                                            sq.where(flt)
                                                        )
            if flt is not None:
                tail_filters.append(flt)

        for tail_filter in tail_filters:
            query = query.where(tail_filter)

        if cursor:
            query = query.where(
                sql_or(
                    TargetVulnRow.criticality < cursor[0],
                    sql_and(
                        TargetVulnRow.criticality == cursor[0],
                        sql_or(
                            TargetVulnRow.severity < cursor[1],
                            sql_and(
                                TargetVulnRow.severity == cursor[1],
                                sql_or(
                                    TargetVulnRow.name > cursor[2],
                                    sql_and(
                                        TargetVulnRow.name == cursor[2],
                                        TargetVulnRow.vuln_id >= cursor[3],
                                    ),
                                ),
                            ),
                        ),
                    ),
                )
            )
        query = query.order_by(
            sql_desc(TargetVulnRow.criticality),
            sql_desc(TargetVulnRow.severity),
            TargetVulnRow.name,
            TargetVulnRow.vuln_id,
        )
        query = query.limit(limit + 1)
        next_cursor = None
        with Connection(self.current_shard_name) as (db_connection):
            vulnerabilities = db_connection.execute(query).fetchall()
            vulns = []
            len_vulns = 0
            if vulnerabilities is not None:
                for vuln in vulnerabilities:
                    len_vulns += 1
                    if len_vulns == limit:
                        next_cursor = (
                            vuln.criticality,
                            vuln.severity,
                            vuln.name,
                            vuln.vuln_id,
                        )
                        break
                    if vuln.status == "open":
                        if vuln.rediscovered:
                            status = "rediscovered"
                        status = vuln.status
                    vuln_tags = set(vuln.tags) if vuln.tags else set()
                    vuln_type_tags = set(vuln.vt_tags) if vuln.vt_tags else set()
                    tags = vuln_tags.union(vuln_type_tags)
                    v = dict(
                        last_seen=vuln.last_seen,
                        status=status,
                        tags=list(tags),
                        vuln_id=str(vuln.vuln_id),
                        target_id=vuln.target_id,
                        severity=vuln.severity,
                        affects_detail=vuln.loc_detail,
                        vt_id=vuln.vt_id,
                        affects_url=vuln.url,
                        vt_name=vuln.name,
                        criticality=vuln.criticality,
                        continuous=vuln.continuous,
                        issue_id=vuln.issue_id,
                        target_description=vuln.target_description,
                    )
                    vulns.append(ClsVulnerability(**v))

        if next_cursor is not None:
            next_cursor = base64.b64encode(json.dumps(next_cursor).encode()).decode()
        return ClsVulnerabilityListResponse(
            vulnerabilities=vulns,
            pagination=ClsPagination(previous_cursor=c, next_cursor=next_cursor),
        )

    def get_vulnerability_http_response(self, vuln_id, **_):
        vuln_id = int(vuln_id)
        joined_tables = []
        select_from, tail_filters = generate_target_vulns_query(
            self.request_user, joined_tables
        )
        q = (
            sql_select((TargetVulnRow.vuln_hash,))
            .select_from(select_from)
            .where(TargetVulnRow.vuln_id == vuln_id)
        )
        vs = None
        with Connection(self.current_shard_name) as (db):
            r = db.execute(q).fetchone()
            if not r:
                raise Http404()
            vs = r.vuln_hash.replace("-", "")
        if is_setup_type_on_premise():
            base_storage = settings.get("base_storage")
            filename = os.path.join(base_storage, "http_resp", vs[0], vs[1], vs[2], vs)
            if not os.path.exists(filename):
                raise Http404()
            return FileDownload(
                filename,
                headers={"Content-Encoding": "gzip"},
                mime_type="text/plain",
                open_file_mode="rb",
            )
        if is_setup_type_aws():
            raise Http404()
        raise Http404()

    def get_vulnerability_details(self, vuln_id, **_):
        """
        Permissions: any_role & (access_all | access_group)
        :param vuln_id:
        :param _:
        :return:
        """
        vuln_id = int(vuln_id)
        joined_tables = []
        select_from, tail_filters = generate_target_vulns_query(
            self.request_user, joined_tables
        )
        select_from = select_from.join(
            VulnTypesTable, VulnTypeRow.vt_id == TargetVulnRow.vt_id
        )
        query = sql_select(
            (
                TargetVulnRow.request,
                TargetVulnRow.continuous,
                TargetVulnRow.tags,
                VulnTypeRow.tags.label("vt_tags"),
                VulnTypeRow.name,
                TargetVulnRow.vt_id,
                TargetVulnRow.target_id,
                TargetVulnRow.criticality,
                TargetVulnRow.status,
                TargetVulnRow.rediscovered,
                TargetVulnRow.url,
                VulnTypeRow.severity,
                TargetVulnRow.loc_detail,
                TargetVulnRow.first_seen,
                TargetVulnRow.last_seen,
                TargetVulnRow.details,
                TargetVulnRow.sensor_details,
                VulnTypeRow.app_id,
                VulnTypeRow.impact,
                VulnTypeRow.description,
                VulnTypeRow.long_description,
                VulnTypeRow.recommendation,
                VulnTypeRow.refs,
                VulnTypeRow.cvss2,
                VulnTypeRow.cvss3,
                VulnTypeRow.cvss_score,
                TargetVulnRow.source,
                VulnTypeRow.details_template,
                TargetVulnRow.details_type,
                TargetVulnRow.issue_id,
                TargetVulnRow.vuln_hash,
                TargetVulnRow.http_response,
                TargetRow.description.label("target_description"),
                TargetRow.type.label("target_type"),
            )
        ).select_from(select_from)
        for tail_filter in tail_filters:
            query = query.where(tail_filter)

        query = query.where(TargetVulnRow.vuln_id == vuln_id)
        with Connection(self.current_shard_name) as (db_connection):
            vuln = db_connection.execute(query).fetchone()
            if not vuln:
                return Http404()
            if vuln.status == "open":
                if vuln.rediscovered:
                    status = "rediscovered"
                status = vuln.status
            vuln_tags = set(vuln.tags) if vuln.tags else set()
            vuln_type_tags = set(vuln.vt_tags) if vuln.vt_tags else set()
            tags = vuln_tags.union(vuln_type_tags)
            references = []
            if vuln.refs:
                for ref in vuln.refs:
                    references.append(ClsLink(rel=ref[0], href=ref[1]))

            details = vuln.details
            try:
                if vuln.details_type == "json":
                    details = pystache.render(
                        vuln.details_template, json.loads(details)
                    )
            except Exception as e:
                logger.exception("render details failed with %s for %s", e, vuln)

            try:
                if vuln.sensor_details:
                    sensor_details = pystache.render(
                        sensor_details_template, json.loads(vuln.sensor_details)
                    )
                    if not details:
                        details = sensor_details
                    else:
                        details += sensor_details
            except Exception as e:
                logger.exception("render sensor details failed with %s for %s", e, vuln)

            response_info = False
            if is_setup_type_on_premise():
                base_storage = settings.get("base_storage")
                vs = vuln.vuln_hash.replace("-", "")
                filename = os.path.join(
                    base_storage, "http_resp", vs[0], vs[1], vs[2], vs
                )
                response_info = os.path.exists(filename)
            else:
                if is_setup_type_aws():
                    if vuln.http_response is not None:
                        from helpers.aws.s3 import generate_link

                        vs = (
                            vuln.target_id.replace("-", "")
                            + "/"
                            + vuln.vuln_hash.replace("-", "")
                        )
                        response_info = generate_link(
                            settings["storage.http_responses.s3.bucket"], vs, 600
                        )
            data = dict(
                request=vuln.request,
                tags=list(tags),
                loc_id=None,
                vt_id=vuln.vt_id,
                target_id=vuln.target_id,
                criticality=vuln.criticality,
                status=status,
                affects_url=vuln.url,
                vt_name=vuln.name,
                severity=vuln.severity,
                affects_detail=vuln.loc_detail,
                vuln_id=str(vuln_id),
                last_seen=vuln.last_seen,
                impact=vuln.impact,
                description=vuln.description,
                long_description=vuln.long_description,
                recommendation=vuln.recommendation,
                references=references,
                cvss2=vuln.cvss2,
                cvss3=vuln.cvss3,
                cvss_score=vuln.cvss_score,
                details=details,
                continuous=vuln.continuous,
                source=vuln.source,
                issue_id=vuln.issue_id,
                target_description=vuln.target_description,
                response_info=response_info,
            )
        return ClsVulnerabilityDetails(**data)

    def set_vulnerability_status(self, vuln_id, body, **_):
        """
        Permissions: tester & (access_all | access_group)
        :param vuln_id:
        :param body:
        :param _:
        :return:
        """
        vuln_id = int(vuln_id)
        require_at_least_tester(self.request_user)
        status = ClsVulnerabilityStatus(**body)
        select_from, tail_filters = generate_target_vulns_query(self.request_user)
        query = sql_select((TargetVulnRow.vuln_id,)).select_from(select_from)
        for tail_filter in tail_filters:
            query = query.where(tail_filter)

        update_values = dict(status=status.status)
        if status.status == "fixed":
            update_values["fixed_at"] = sql_func.now()
        update_query = (
            (TargetVulnsTable.update(values=update_values))
            .where(TargetVulnRow.vuln_id == vuln_id)
            .where(TargetVulnRow.vuln_id.in_(query))
            .where(TargetVulnRow.status != status.status)
            .returning(TargetVulnRow.target_id)
        )
        with Connection(self.current_shard_name) as (db_connection):
            result = db_connection.execute(update_query).scalar()
            if result:
                event_query = create_event(
                    "vulnerability_marked_as",
                    get_owner_id(self.request_user),
                    self.request_user["user_id"],
                    data=dict(target_id=result, status=status.status),
                    resource_type=EventResources.vulnerability,
                    resource_id=str(vuln_id),
                    shard=self.current_shard_name,
                )
                db_connection.execute(event_query)
        return Http204()

    def get_vulnerability_type(self, vt_id, **_):
        if not self.current_shard_name:
            raise AssertionError
        if not self.request_user:
            raise AssertionError
        query = sql_select(
            (
                VulnTypeRow.cvss3,
                VulnTypeRow.cvss2,
                VulnTypeRow.name,
                VulnTypeRow.tags,
                VulnTypeRow.severity,
                VulnTypeRow.vt_id,
            )
        ).where(VulnTypeRow.vt_id == vt_id)
        with Connection(self.current_shard_name) as (db_connection):
            vuln_type = db_connection.execute(query).fetchone()
            if vuln_type is None:
                return Http404()
            return ClsVulnerabilityType(**dict(vuln_type))

    def get_vulnerability_types(self, q=None, l=None, c=None, v="default", **_):
        """
        Vulnerabilities list, only those actually present in TargetVulnsTable
        Permissions: any_role & (access_all | access_group)
        :param q:
        :param l:
        :param c:
        :param v:
        :param _:
        :return:
        """
        if not self.current_shard_name:
            raise AssertionError
        if not self.request_user:
            raise AssertionError
        limit = min(l, 100) if l is not None else 100
        c = int(c) if c is not None else 0
        cursor = c
        filters = Filters(vulnerability_list_filters, q)
        columns = [
            VulnTypeRow.cvss3,
            VulnTypeRow.cvss2,
            VulnTypeRow.name,
            VulnTypeRow.tags,
            VulnTypeRow.severity,
            VulnTypeRow.vt_id,
            sql_func.count(VulnTypeRow.vt_id).label("count"),
        ]
        if v == "criticality":
            columns.append(TargetVulnRow.criticality)
        tail_filters = []
        if self.request_user["owner_id"] is None:
            from_clause = VulnTypesTable
            from_clause = from_clause.join(
                TargetVulnsTable, TargetVulnRow.vt_id == VulnTypeRow.vt_id
            )
            from_clause = from_clause.join(
                TargetsTable, TargetRow.target_id == TargetVulnRow.target_id
            )
            tail_filters.append(TargetRow.owner_id == self.request_user["user_id"])
        else:
            if self.request_user["access_all_groups"]:
                from_clause = VulnTypesTable
                from_clause = from_clause.join(
                    TargetVulnsTable, TargetVulnRow.vt_id == VulnTypeRow.vt_id
                )
                from_clause = from_clause.join(
                    TargetsTable, TargetRow.target_id == TargetVulnRow.target_id
                )
                tail_filters.append(TargetRow.owner_id == self.request_user["owner_id"])
            else:
                from_clause = VulnTypesTable
                from_clause = from_clause.join(
                    TargetVulnsTable, TargetVulnRow.vt_id == VulnTypeRow.vt_id
                )
                from_clause = from_clause.join(
                    TargetsTable, TargetRow.target_id == TargetVulnRow.target_id
                )
                tail_filters.append(
                    TargetRow.target_id.in_(
                        get_distinct_targets_id_by_group(self.request_user)
                    )
                )
            for filter_key, filter_expression in filters.data.items():
                flt = None
                if filter_key == "criticality":
                    flt = get_sqlachemy_filter(
                        filter_expression,
                        TargetVulnRow.criticality,
                        filters.descriptor["keys"][filter_key],
                    )
                else:
                    if filter_key == "severity":
                        flt = get_sqlachemy_filter(
                            filter_expression,
                            VulnTypeRow.severity,
                            filters.descriptor["keys"][filter_key],
                        )
                    else:
                        if filter_key == "status":
                            flt = get_sqlachemy_filter(
                                filter_expression,
                                TargetVulnRow.status,
                                filters.descriptor["keys"][filter_key],
                            )
                        else:
                            if filter_key == "cvss_score":
                                flt = get_sqlachemy_filter(
                                    filter_expression,
                                    VulnTypeRow.cvss_score,
                                    filters.descriptor["keys"][filter_key],
                                )
                            else:
                                if filter_key == "rediscovered":
                                    flt = get_sqlachemy_filter(
                                        filter_expression,
                                        TargetVulnRow.rediscovered,
                                        filters.descriptor["keys"][filter_key],
                                    )
                                else:
                                    if filter_key == "continuous":
                                        flt = get_sqlachemy_filter(
                                            filter_expression,
                                            TargetVulnRow.continuous,
                                            filters.descriptor["keys"][filter_key],
                                        )
                                    else:
                                        if filter_key == "target_id":
                                            flt = get_sqlachemy_filter(
                                                filter_expression,
                                                TargetVulnRow.target_id,
                                                filters.descriptor["keys"][filter_key],
                                            )
                                        else:
                                            if filter_key == "vt_id":
                                                flt = get_sqlachemy_filter(
                                                    filter_expression,
                                                    VulnTypeRow.vt_id,
                                                    filters.descriptor["keys"][
                                                        filter_key
                                                    ],
                                                )
                                            else:
                                                if filter_key == "date":
                                                    flt = get_sqlachemy_filter(
                                                        filter_expression,
                                                        TargetVulnRow.last_seen,
                                                        filters.descriptor["keys"][
                                                            filter_key
                                                        ],
                                                    )
                                                else:
                                                    if filter_key == "group_id":
                                                        flt = get_sqlachemy_filter(
                                                            filter_expression,
                                                            Target2GroupRow.group_id,
                                                            filters.descriptor["keys"][
                                                                filter_key
                                                            ],
                                                        )
                                                        sf, tf = generate_target2group_query(
                                                            self.request_user
                                                        )
                                                        sq = sql_select(
                                                            (Target2GroupRow.target_id,)
                                                        ).select_from(sf)
                                                        sq = apply_filters(sq, tf)
                                                        flt = TargetVulnRow.target_id.in_(
                                                            sq.where(flt)
                                                        )
                if flt is not None:
                    tail_filters.append(flt)

            select = sql_select(columns).select_from(from_clause)
            for tail_filter in tail_filters:
                select = select.where(tail_filter)

            select = select.where(TargetRow.deleted_at.is_(None))
            if v == "default":
                select = select.group_by(VulnTypeRow.vt_id)
            else:
                if v == "criticality":
                    select = select.group_by(
                        TargetVulnRow.criticality, VulnTypeRow.vt_id
                    )
            select = select.limit(limit + 1).offset(cursor)
            if v == "default":
                select = select.order_by(
                    sql_desc(VulnTypeRow.severity), VulnTypeRow.name, VulnTypeRow.vt_id
                )
            else:
                if v == "criticality":
                    select = select.order_by(
                        sql_desc(TargetVulnRow.criticality),
                        sql_desc(VulnTypeRow.severity),
                        VulnTypeRow.name,
                        VulnTypeRow.vt_id,
                    )
                else:
                    select = select.order_by(VulnTypeRow.vt_id)
                curred_vuln_types = []
                with Connection(self.current_shard_name) as (db_connection):
                    l = 0
                    for x in db_connection.execute(select).fetchall():
                        l += 1
                        if l <= limit:
                            curred_vuln_types.append(
                                ClsVulnerabilityTypeTargetsCountResponseItem(**dict(x))
                            )

                if l > limit:
                    next_cursor = cursor + l
                else:
                    next_cursor = None
                return ClsVulnerabilityTypeTargetsCountResponse(
                    vulnerability_types=curred_vuln_types,
                    pagination=ClsPagination(
                        previous_cursor=cursor, next_cursor=next_cursor
                    ),
                )

    def create_issue(self, vuln_id, **_):
        require_at_least_tester(self.request_user)
        joined_tables = []
        select_from, tail_filters = generate_target_vulns_query(
            self.request_user, joined_tables
        )
        select_from = select_from.join(
            VulnTypesTable, TargetVulnRow.vt_id == VulnTypeRow.vt_id
        )
        query = sql_select(
            (
                TargetVulnRow.issue_id,
                TargetVulnRow.vuln_id,
                TargetVulnRow.target_id,
                TargetRow.address.label("target_address"),
                TargetVulnRow.scan_session_id,
                TargetVulnRow.url,
                TargetVulnRow.loc_detail,
                TargetVulnRow.details,
                TargetVulnRow.details_type,
                TargetVulnRow.first_seen,
                TargetVulnRow.last_seen,
                TargetVulnRow.tags,
                TargetVulnRow.sensor_details,
                VulnTypeRow.tags.label("vt_tags"),
                TargetVulnRow.request,
                TargetVulnRow.source,
                TargetVulnRow.vt_id,
                VulnTypeRow.name.label("vt_name"),
                VulnTypeRow.severity,
                VulnTypeRow.impact,
                VulnTypeRow.description,
                VulnTypeRow.recommendation,
                VulnTypeRow.long_description,
                VulnTypeRow.cvss2,
                VulnTypeRow.cvss3,
                VulnTypeRow.cvss_score,
                VulnTypeRow.refs,
                VulnTypeRow.details_template,
            )
        ).select_from(select_from)
        tail_filters.append(TargetVulnRow.vuln_id == vuln_id)
        query = apply_filters(query, tail_filters)
        with Connection(self.current_shard_name) as (db_connection):
            vuln_info = db_connection.execute(query).fetchone()
            if not vuln_info:
                return Http404()
            if vuln_info.issue_id:
                return Http204()
            issue_tracker_query = (
                sql_select((TargetConfigurationRow.value,))
                .where(TargetConfigurationRow.target_id == vuln_info.target_id)
                .where(TargetConfigurationRow.name == "issue_tracker_id")
            )
            issue_tracker_id = db_connection.execute(issue_tracker_query).fetchone()
            if not issue_tracker_id:
                return Http409(
                    "There is no issue tracker configured for the current target"
                )
            issue_tracker_id = issue_tracker_id.value
            issue_tracker_query = sql_select((IssueTrackerRow,)).where(
                IssueTrackerRow.issue_tracker_id == issue_tracker_id
            )
            issue_tracker_info = db_connection.execute(issue_tracker_query).fetchone()
            issue_tracker_info = issue_tracker_info.data
            vuln_info = dict(vuln_info)
        details_type = vuln_info["details_type"]
        details_template = vuln_info["details_template"]
        del vuln_info["details_type"]
        del vuln_info["details_template"]
        try:
            if details_type == "json":
                vuln_info["details"] = pystache.render(
                    details_template, json.loads(vuln_info["details"])
                )
        except Exception as e:
            logger.exception("render details failed with %s for %s", e, vuln_info)

        try:
            if vuln_info["sensor_details"]:
                sensor_details = pystache.render(
                    sensor_details_template, json.loads(vuln_info["sensor_details"])
                )
                if not vuln_info["details"]:
                    vuln_info["details"] = sensor_details
                else:
                    vuln_info["details"] += sensor_details
        except Exception as e:
            logger.exception(
                "render sensor details failed with %s for %s", e, vuln_info
            )

        try:
            client = issue_tracker_get_client(
                issue_tracker_info,
                settings.get("system_proxy_cb", None),
                settings.get("ssl_ca_bundle_path", True),
            )
            issue_id = client.add_vulnerability(vuln_data=vuln_info)
            if not issue_id:
                return Http500("Issue tracker: Error")
        except issue_tracker_exceptions.ConfigError as e:
            return Http500(str(e))
        except ConnectionError:
            return Http500("Issue tracker: Connection error")
        except (requests.Timeout, requests.ReadTimeout):
            return Http500("Issue tracker: Connection timeout")
        except requests.URLRequired:
            return Http500("Issue tracker: Invalid url")
        except requests.RequestException:
            return Http500("Issue tracker: Request exception")
        except Exception as e:
            logger.exception(e)
            return Http500("Issue tracker: Connection error")

        update_query = (
            (
                TargetVulnsTable.update(
                    values=dict(issue_id=issue_id, issue_tracker_id=issue_tracker_id)
                )
            )
            .where(TargetVulnRow.target_id == vuln_info["target_id"])
            .where(TargetVulnRow.vuln_id == vuln_id)
            .where(TargetVulnRow.issue_id.is_(None))
        )
        with Connection(self.current_shard_name) as (db_connection):
            db_connection.execute(update_query)
        return Http204()

    def recheck_vulnerability(self, vuln_id, body, **_):
        """
        :param body:
        :param vuln_id:
        :param _:
        :return:
        """
        vulnerability_recheck = ClsVulnerabilityRecheck(**body)
        require_at_least_tester(self.request_user)
        self.license.require(Features.VULN_RETEST)
        joined_tables = []
        select_from, tail_filters = generate_target_vulns_query(
            self.request_user, joined_tables
        )
        select_from = select_from.join(
            VulnTypesTable, VulnTypeRow.vt_id == TargetVulnRow.vt_id
        )
        select_from = select_from.outerjoin(
            ScanSessionsTable,
            ScanSessionRow.scan_session_id == TargetVulnRow.scan_session_id,
        )
        select_from = select_from.outerjoin(
            ScansTable, ScanRow.scan_id == ScanSessionRow.scan_id
        )
        select_from = select_from.join(
            TargetsWithExcludedHoursViewTable,
            TargetsWithExcludedHoursViewRow.target_id == TargetVulnRow.target_id,
        )
        query = sql_select(
            (
                TargetRow.target_id,
                TargetRow.address,
                TargetRow.description,
                TargetRow.manual_intervention,
                TargetsWithExcludedHoursViewRow.xh_data,
                TargetsWithExcludedHoursViewRow.xh_time_offset,
                VulnTypeRow.name,
                VulnTypeRow.app_id,
                TargetVulnRow.source,
                ScanRow.profile_id,
            )
        ).select_from(select_from)
        query = apply_filters(query, tail_filters).where(
            TargetVulnRow.vuln_id == vuln_id
        )
        with Connection(self.current_shard_name) as (db):
            vuln_info = db.execute(query).fetchone()
            if not vuln_info:
                raise Http404()
            system_excluded_hours = get_system_excluded_hours(db)
            target_xh_data = vuln_info.xh_data
            target_xh_time_offset = vuln_info.xh_time_offset
            ui_session_id = None
            if has_feature(SetupFeatures.MANUAL_BROWSING):
                if vuln_info.manual_intervention:
                    ui_session_id = vulnerability_recheck.get("ui_session_id")
                    if ui_session_id is None:
                        raise Http409(
                            "target requires manual intervention",
                            code=ApiErrors.MI_NO_SESSION_ID,
                        )
                    if self.remote_address not in get_local_addresses():
                        raise Http409(
                            "target requires manual intervention",
                            code=ApiErrors.MI_NOT_FROM_LOCALHOST,
                        )
                    if target_xh_data:
                        if not is_allowed_to_scan_now(
                            target_xh_data, target_xh_time_offset
                        ):
                            raise Http409(
                                "target requires manual intervention",
                                code=ApiErrors.MI_SCAN_NOW_NOT_POSSIBLE_TARGET_EXCLUDED_HOURS,
                            )
                        else:
                            if system_excluded_hours:
                                if not (
                                    is_allowed_to_scan_now(**system_excluded_hours)
                                ):
                                    raise Http409(
                                        "target requires manual intervention",
                                        code=ApiErrors.MI_SCAN_NOW_NOT_POSSIBLE_SYSTEM_EXCLUDED_HOURS,
                                    )
                        profile_id = generate_profile_for_vuln_recheck(
                            db=db,
                            source=vuln_info["source"],
                            creator_id=self.request_user["user_id"],
                            owner_id=get_owner_id(self.request_user),
                            original_profile_id=vuln_info["profile_id"],
                        )
                        if not profile_id:
                            raise Http409("vuln type not supported")
                        scan_id = str(uuid.uuid4())
                        scan_data = dict(
                            scan_id=scan_id,
                            owner_id=get_owner_id(self.request_user),
                            creator_id=self.request_user["user_id"],
                            target_id=vuln_info["target_id"],
                            profile_id=profile_id,
                            recurrence=None,
                            schedule_disabled=False,
                            history_limit=1,
                            schedule_start_date=None,
                            next_run=sql_func.now(),
                            schedule_time_sensitive=True,
                            report_template_id=None,
                        )
                        query_scan = ScansTable.insert().values(**scan_data)
                        db.execute(query_scan)
                        headers = None
                        if target_xh_data:
                            if not is_allowed_to_scan_now(
                                target_xh_data, target_xh_time_offset
                            ):
                                headers = {
                                    "X-Schedule-Delayed": str(
                                        get_next_date_allowed_to_scan(
                                            target_xh_data, target_xh_time_offset
                                        )
                                    )
                                }
                            else:
                                if system_excluded_hours:
                                    if not (
                                        is_allowed_to_scan_now(**system_excluded_hours)
                                    ):
                                        headers = {
                                            "X-Schedule-Delayed": str(
                                                get_next_date_allowed_to_scan(
                                                    **system_excluded_hours
                                                )
                                            )
                                        }
                            scan = dict(
                                scan_id=scan_id,
                                targetd=dict(
                                    target_id=vuln_info.target_id,
                                    address=vuln_info.address,
                                    description=vuln_info.description,
                                ),
                            )
                            if ui_session_id:
                                scan["ui_session_id"] = ui_session_id
                            if has_feature(SetupFeatures.MANUAL_BROWSING):
                                if vuln_info.manual_intervention:
                                    settings[
                                        "manual_browsing_storage"
                                    ].register_ui_session_id(scan_id, ui_session_id)
                            return Http201(
                                location=self.base_path
                                + "/scans/"
                                + scan_data["scan_id"],
                                body=scan,
                                headers=headers,
                            )
